#include "timer.h"
#include "common.h"
#include <assert.h>
#include "memalloc.h"

#define time_after(unknown,known) ((long)(known)-(long)(unknown)<0)
#define time_before(unknown,known) ((long)(unknown)-(long)(known)<0)
#define time_after_eq(unknown,known) ((long)(unknown)-(long)(known)>=0)
#define time_before_eq(unknown,known) ((long)(known)-(long)(unknown)>=0)

namespace Cross
{

struct GTimerList
{
  list_head lst;
  uint32 expire;
  uint32 delta;
  GTimerFunc func;
  void* data;
  GDeleteFunc delFunc;

  DEFINE_OWN_MEM_OPERATION
};

GTimerList* GlobalTimer::CreateTimer(uint32 dt,GTimerFunc func,void* data,GDeleteFunc delFunc)
{
  GTimerList* tl=new GTimerList;
  INIT_LIST_HEAD(&tl->lst);
  tl->expire=mGTick;
  tl->delta=dt;
  tl->func=func;
  tl->data=data;
  tl->delFunc=delFunc;
  return tl;
}

void GlobalTimer::DestroyTimer(GTimerList* tl)
{
  if(tl->delFunc)
      tl->delFunc(tl->data);
  delete tl;
}

GlobalTimer::RotaryTbl::RotaryTbl()
{
  for(int i=0;i<(int)TM_SIZE;++i)
  {
    INIT_LIST_HEAD(&entry[i]);
  }
  idx=0;
}

GlobalTimer::RotaryTbl::~RotaryTbl()
{
  for(int i=0;i<(int)TM_SIZE;++i)
  {
    list_head* pos;
    list_head* n;
    list_for_each_safe(pos,n,&entry[i])
    {
      GTimerList* tl=list_entry(pos,GTimerList,lst);
      list_del(pos);
      GlobalTimer::DestroyTimer(tl);
    }
  }
}

GlobalTimer::GlobalTimer(uint32 gtick)
{
  mGTick=gtick;
  mLastTick=gtick;
}

void GlobalTimer::AddTimer(GTimerList* tl)
{
  tl->expire+=tl->delta;
  AddTimerImpl(tl);
}

void GlobalTimer::AddTimerImpl(GTimerList* tl)
{
  uint32 dt=0;
  if(time_after(tl->expire,mGTick))
    dt=tl->expire-mGTick;
  list_head* entry;
  if(dt<TM_SIZE)
  {
    int idx=(mRotaryTbls[0].idx+dt)&TM_MASK;
    entry=&mRotaryTbls[0].entry[idx];
  }
  else if(dt<TM_SIZE*TM_SIZE)
  {
    dt-=TM_SIZE;
    int idx=(mRotaryTbls[1].idx+(dt>>TM_BITS))&TM_MASK;
    entry=&mRotaryTbls[1].entry[idx];
  }
  else if(dt<TM_SIZE*TM_SIZE*TM_SIZE)
  {
    dt-=TM_SIZE*TM_SIZE;
    int idx=(mRotaryTbls[2].idx+(dt>>(TM_BITS*2)))&TM_MASK;
    entry=&mRotaryTbls[2].entry[idx];
  }
  else
  {
    dt-=TM_SIZE*TM_SIZE*TM_SIZE;
    int idx=(mRotaryTbls[3].idx+(dt>>(TM_BITS*3)))&TM_MASK;
    entry=&mRotaryTbls[3].entry[idx];
  }
  assert(list_empty(&tl->lst));
  list_add_tail(&tl->lst,entry);
}

void GlobalTimer::Tick(uint32 gtick)
{
  mGTick=gtick;
  if(time_after(mGTick,mLastTick))
  {
    uint32 dt=mGTick-mLastTick;
    while(dt-->0)
    {
      OneTick();
    }
  }
  mLastTick=mGTick;
}

void GlobalTimer::OneTick()
{
  assert(mRotaryTbls[0].idx>=0&&mRotaryTbls[0].idx<(int)TM_SIZE);
  list_head* entry=&mRotaryTbls[0].entry[mRotaryTbls[0].idx++];
  LIST_HEAD(tmphead);
  list_splice_init(entry,&tmphead);
  list_head* pos;
  list_head* n;
  list_for_each_safe(pos,n,&tmphead)
  {
    GTimerList* tl=list_entry(pos,GTimerList,lst);
    list_del_init(pos);
    if(tl->func)
      tl->func(this,tl,tl->data);
    if(list_empty(&tl->lst))
      GlobalTimer::DestroyTimer(tl);
  }
  for(int i=0;i<((int)sizeofa(mRotaryTbls)-1)&&mRotaryTbls[i].idx>=(int)TM_SIZE;++i)
  {
    mRotaryTbls[i].idx=0;
    Cascade(&mRotaryTbls[i+1].entry[mRotaryTbls[i+1].idx++]);
  }
  if(mRotaryTbls[sizeofa(mRotaryTbls)-1].idx>=(int)TM_SIZE)
    mRotaryTbls[sizeofa(mRotaryTbls)-1].idx=0;
}

void GlobalTimer::Cascade(list_head* tmlst)
{
  list_head* pos;
  list_head* n;
  list_for_each_safe(pos,n,tmlst)
  {
    GTimerList* tl=list_entry(pos,GTimerList,lst);
    list_del_init(pos);
    AddTimerImpl(tl);
  }
}

}
